#pragma once

#include "Business/Business.h"
#include "StatusMachine/StatusMachine.h"
#include "Business/Leg.h"
#include "Business/Dealing.h"
#include "Utils/Callbackable.h"
#include "Business/KLine/KLine.h"
#include "Business/DealingHistory.h"

class MarketStorage;
class QueueContext;
class DealingRepository;
class CancellationRepository;
class TimeoutNotifier;
class TrendBusiness;
class Market;

namespace TrendBusinessStatusMachine {
	class Event
	{
	public:
		enum TYPE {NONE,MARKET_EVENT, DEALING_EVENT,STOP_COMMAND,START_COMMAND, EDIT_COMMAND,CLEAR_COMMAND,CANCEL_COMMAND};
		explicit Event(TYPE type = NONE);
		explicit Event(Market const& market);
		explicit Event(Dealing const& dealing);
		int GetType() const { return m_type; }
		Market const& GetMarket() const { return *(Market*)m_data; }
		Dealing const& GetDealing() const { return *(Dealing*)m_data; }

	private:
		int m_type;
		char m_data[1024];
	};

	typedef Status<TrendBusiness,Event> StatusT;
	class StatusBase : public StatusT
	{
	public:
		StatusBase(std::string const& name, TrendBusiness& context):StatusT(name,context){}
	private:
		virtual void DoBefore();
	};
	typedef ConditionBase<TrendBusiness,Event> ConditionT;
	typedef StatusMachine<TrendBusiness,Event> StatusMachineT;


#define DEFINE_CONDITION(condition_name)						\
	class condition_name : public ConditionT					\
	{															\
	public:														\
	explicit condition_name(TrendBusiness& context)	\
	:ConditionT(context){}										\
	virtual ~condition_name() {}								\
	private:													\
	virtual bool DoCheck(Event const& event);					\
	}
	DEFINE_CONDITION(OpenSignalCondition);
	DEFINE_CONDITION(OpenOrderedCondition);
	DEFINE_CONDITION(OpenAllCancelledAndNoPostionCondition);
	DEFINE_CONDITION(OpenedCondition);
	DEFINE_CONDITION(OffsetAllCondition);
	DEFINE_CONDITION(GainCondition);
	DEFINE_CONDITION(OffsetOrderedCondition);
	DEFINE_CONDITION(GainFinishedAndHasPositionCondition);
	DEFINE_CONDITION(AllOffsetedCondition);
	DEFINE_CONDITION(StopCondition);
	DEFINE_CONDITION(EditCondition);
	DEFINE_CONDITION(StartCondition);
	DEFINE_CONDITION(ClearCondition);
	DEFINE_CONDITION(CancelCondtion);
#undef DEFINE_CONDITION

#define DEFINE_STATUS(class_name,name)					\
	class class_name : public StatusBase					\
	{														\
	public:													\
	explicit class_name(TrendBusiness& context)		\
	:StatusBase(name,context){}							\
	virtual ~class_name(){}								\
	private:											\
	virtual void DoExecute(Event const& event);			\
	}

	DEFINE_STATUS(EditableStatus,"editable");
	DEFINE_STATUS(StartStatus,"start");
	DEFINE_STATUS(OpenSentStatus,"open sent");
	DEFINE_STATUS(CancelSentStatus,"open cancel sent");
	DEFINE_STATUS(OpenedStatus,"opened");
	DEFINE_STATUS(OffsetAllSentStatus,"offset all sent");
	DEFINE_STATUS(GainSentStatus,"offset gain sent");
	DEFINE_STATUS(OffsetGainCancelSentStatus,"offset gain cancel sent");
	DEFINE_STATUS(ClearSentStatus,"clear sent");
	DEFINE_STATUS(StopStatus,"stop");
#undef DEFINE_STATUS

}

class TrendArgument
{
public:
	TrendArgument(int contract, int openQuantity, int minute,double maxGain, double maxLoss)
		:m_contract(contract),m_openQuantity(openQuantity),m_minute(minute),m_maxGain(maxGain),m_maxLoss(maxLoss)
	{
	}
	int GetContract() const { return m_contract; }
	int GetOpenQuantity() const { return m_openQuantity; }
	size_t GetMinute() const { return m_minute; }
	double GetMaxGain() const { return m_maxGain; }
	double GetMaxLoss() const { return m_maxLoss; }
private:
	int m_contract;
	int m_openQuantity;
	size_t m_minute;
	double m_maxGain;
	double m_maxLoss;
};

class TrendBusinessSnapshot 
{
public:
	TrendBusinessSnapshot(char const* status,Direction::TYPE direction,int quantity,double price)
		:m_status(status),m_direction(direction),m_quantity(quantity),m_price(price)
	{
	}
	char const* GetStatus() const {return m_status;}
	int GetQuantity() const {return m_quantity;}
	double GetPrice() const {return m_price;}
	Direction::TYPE GetDirection() const { return m_direction; }

	bool operator==(TrendBusinessSnapshot const& other) const
	{
		return m_status == other.m_status && m_quantity == other.m_quantity && m_price == other.m_price;
	}
	bool operator!=(TrendBusinessSnapshot const& other) const
	{
		return !operator==(other);
	}
private:
	char const * m_status;
	Direction::TYPE m_direction;
	int m_quantity;
	double m_price;

};

class TrendBusiness : public AutoUpdateUIBusiness<TrendBusiness>
{
public:
	typedef TrendArgument Argument;
	typedef TrendBusinessSnapshot Snapshot;
	struct OffsetPrice
	{
		double offsetAllPrice;
		double gainPrice;
	};

private:
	class Legs
	{
	public:
		Legs(Market const& market, Direction::TYPE direction)
			:m_openLeg(market,direction,Operation::OPEN),
			m_stopLeg(market,m_openLeg.NegateDirection(),Operation::OFFSET_TODAY)
		{
		}
		Leg const& GetOpenLeg() const { return m_openLeg; }
		Leg const& GetStopLeg() const { return m_stopLeg; }

		bool CanStop(double stopPrice) 
		{
			if (m_openLeg.GetDirection() == Direction::BUY)
			{
				return m_stopLeg.GetPrice() <= stopPrice;
			}
			else
			{
				return m_stopLeg.GetPrice() >= stopPrice;
			}

		}

		bool CanOffset(double offsetPrice) 
		{
			if (m_openLeg.GetDirection() == Direction::BUY)
			{
				return m_stopLeg.GetPrice() >= offsetPrice;
			}
			else
			{
				return m_stopLeg.GetPrice() <= offsetPrice;
			}
		}
		double GetOpenPrice()
		{
			return m_openLeg.GetPrice();
		}
		double GetOffsetPrice(double minProfit)
		{
			if (m_openLeg.GetDirection() == Direction::BUY)
			{
				return m_stopLeg.GetPrice() + minProfit;
			}
			else
			{
				return m_stopLeg.GetPrice() - minProfit;
			}
		}
		double GetStopPrice(double stop)
		{
			if (m_openLeg.GetDirection() == Direction::BUY)
			{
				return m_stopLeg.GetPrice() - stop;
			}
			else
			{
				return m_stopLeg.GetPrice() + stop;
			}
		}
	private:
		Leg m_openLeg;
		Leg m_stopLeg;
	};
	struct OffsetRate
	{
		OffsetRate(){}
		OffsetRate(double a, double p,double pp)
			:offsetAllRate(a),gainRate(p),gainPercent(pp)
		{
		}
		double offsetAllRate;
		double gainRate;
		double gainPercent;
	};

// 	OffsetRate m_offsetRates[5];
//	OffsetPrice m_offsetPrices[5];
	OffsetPrice m_offsetPrices[2];

public:
	TrendBusiness(Customer& customer,
		TrendArgument const& argument,
		MarketStorage const& marketStorage,
		QueueContext& queueContext, 
		DealingRepository& dealingRepository,
		CancellationRepository& cancellationRepository);
	virtual ~TrendBusiness();

	TrendArgument const& GetArgument() const { return m_argument; }

	Market const& GetMarket() const;
	int GetContract() const;
	char const* GetCode() const;
	bool IsEditable() const { return strcmp(m_machine->GetCurrentStatus()->GetName().c_str(), "editable") == 0;}

	TrendBusinessSnapshot GetSnapshot() const;
	void UpdateUI(TrendBusinessSnapshot const& snapshot);

	void Init();

	bool CanOpen(Market const& market);

	bool HasEnoughFund();
	bool HasProfit(Market const& market, Direction::TYPE direction)  const;
	bool CanOffsetAll(Market const& market);
	bool CanGain(Market const& market);
	bool CanStop();

	bool IsBull() const;

	void Open();
	void Cancel();
	void OffsetAll();
	void OffsetGain();
	void CancelOffset();
	void Stop();
	void CancelStop();
	void Clear();

	int GetOpenedPosition() const 
	{
		return m_openedPosition;
	}
	double GetAvgPrice() const
	{
		return m_avgPrice;
	}

	KLine const& GetKLine() const 
	{
		return m_kline;
	}

	double RefTmp3(int n) const;
	double RefTmp4(int n) const;

//	void CalcOffsetPrice(OffsetPrice* offsetPrices, bool isBull) const;
	void CalcOffsetPrice(OffsetPrice* offsetPrices, bool isBull, double openPrice) const;
	double GetMaxGainPrice() const { return m_maxGainPrice; }
	double GetMaxLossPrice() const { return m_maxLossPrice; }
	OffsetPrice const& GetCurrentOffsetPrice() const { return m_offsetPrices[m_currentOffsetRateIndex];}
private:
	int GetMaxOpenCount() const;
	int CalcOpenQuantity() const;
	Order CreateOpenOrder(int quantity) const;
	void UpdateArgument(TrendArgument const& argument);
	int GetStopQuantity();

	virtual void DoOnInit();
	virtual void DoOnMarket(Market const& market);
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket);
	virtual void DoOnDealing(Dealing const& dealing);
	virtual void DoOnTimeout(RequestTimeout const& requestTimeout);
	virtual void DoOnTrade(Trade const& trade);
	virtual void DoOnCommand(Command const& command);
	virtual void DoOnExchangeStatus(int exchangeStatus);
	double CalcTmp3();
	double CalcTmp4();

	bool BullOffsetCondition(double price,double lastPrice) const 
	{
		return lastPrice > price ;
	}

	double CalcBullOffsetPrice(double rate) const
	{
		KLineItem const& ref1 = m_kline.Ref(0);
		KLineItem const& ref2 = m_kline.Ref(1);
		return ref1.GetMinPrice() + rate *(ref1.GetMaxPrice() - ref2.GetMinPrice());
	}

	bool BearOffsetCondition(double price,double lastPrice) const 
	{
		return price > lastPrice;
	}

	double CalcBearOffsetPrice(double rate) const
	{
		KLineItem const& ref1 = m_kline.Ref(0);
		KLineItem const& ref2 = m_kline.Ref(1);
		return ref1.GetMaxPrice() - (rate *(ref2.GetMaxPrice() -ref1.GetMinPrice()));
	}
	bool IsBear() const;
	TrendArgument m_argument;
	MarketStorage const& m_marketStorage;
	QueueContext& m_queueContext;
	DealingRepository& m_dealingRepository;
	CancellationRepository& m_cancellationRepository;
	std::auto_ptr<Legs> m_legs;

	TrendBusinessStatusMachine::StatusMachineT* m_machine;

	DealingHistory m_openDealingHistory;
	DealingHistory m_offsetDealingHistory;

	int m_openedPosition;
	double m_avgPrice;
	int m_exchangeStatus;

	KLine m_kline;
	boost::circular_buffer<double> m_tmp3;
	boost::circular_buffer<double> m_tmp4;
	bool m_isNewKLine;

	double m_maxGainPrice;
	double m_maxLossPrice;
	int m_currentOffsetRateIndex;
};
